package de.jmda.gen.java.naming;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public class ImportManager
{
	/**
	 * Stores a package name. {@link #generateImportStatements()} will not
	 * generate import statements for types from this package.
	 */
	private String packagename;

	/**
	 * Stores pairs of simple type names and their corresponding package name. The
	 * simple type names can be used unqualified in the java type to be generated
	 * because an appropriate type import statement will be returned by {@link
	 * #generateImportStatements()}.
	 */
	private Map<String, String> typeImports = new HashMap<String, String>();

	/**
	 * Stores static imports.
	 */
	private Set<String> staticImports = new TreeSet<String>();
	
	public ImportManager(String packagename)
	{
		super();
		
		if (packagename == null)
		{
			this.packagename = "";
		}
		else
		{
			this.packagename = packagename;
		}
	}

	public ImportManager()
	{
		this(null);
	}

	/**
	 * @return import statements, containing static and non static imports for the
	 *         java type to be generated
	 */
	public StringBuffer generateImportStatements()
	{
		StringBuffer result = new StringBuffer();

		// add static import statements to result
		for (String staticImport : staticImports)
		{
			result.append(
					"import static " + staticImport + ";" + System.lineSeparator());
		}

		// collect qualified type names to import
		Set<String> importClasses = new TreeSet<String>();

		for (String simpleTypeName : typeImports.keySet())
		{
			importClasses.add(typeImports.get(simpleTypeName) + "." + simpleTypeName);
		}

		// add non static, qualified type names to result
		for (String importClass : importClasses)
		{
			result.append("import " + importClass + ";" + System.lineSeparator());
		}

		return result;
	}

	/**
	 * Updates internal data as necessary and calculates string for type
	 * information in generated field or parameter declarations. For types from
	 * package <code>java.lang</code> and for types from the same package as
	 * {@link #packagename} this method will calculate the according
	 * simple name of <code>qualifiedImportTypeName</code>.
	 *
	 * @param qualifiedImportTypeName
	 * @return <code>qualifiedImportTypeName</code> if it has to be used
	 *         qualified in generated code, simple type name if it does not have
	 *         to be used qualified.
	 */
	public String useType(String qualifiedImportTypeName)
	{
		if (qualifiedImportTypeName.startsWith("java.lang"))
		{
			// do not generate import statements for java types from default package
			// java.lang
			return getSimpleNameFromQualifiedTypeName(qualifiedImportTypeName);
		}

		String packagename =
		    getPackageNameFromQualifiedTypeName(qualifiedImportTypeName);

		if (packagename.equals(this.packagename))
		{
			// do not generate import statements for java types from the same package
			// as the java type to be generated
			return getSimpleNameFromQualifiedTypeName(qualifiedImportTypeName);
		}

		String simpleName =
		    getSimpleNameFromQualifiedTypeName(qualifiedImportTypeName).replace(
		    		'$', '.');

		String typeImportPackageName = typeImports.get(simpleName);

		if (typeImportPackageName != null)
		{
			// do not add type import for simple names that are already stored in
			// typeImports
			if (typeImportPackageName.equals(packagename))
			{
				// qualifiedImportTypeName had been added to typeImports before and the
				// type may be used unqualified
				return simpleName;
			}
			else
			{
				return qualifiedImportTypeName;
			}
		}

		typeImports.put(simpleName, packagename);

		return simpleName;
	}

	public String useType(Class<?> clazz)
	{
		return useType(clazz.getName());
	}
	
	public void addStaticImport(String staticImport)
	{
		if (staticImport.endsWith("*"))
		{
			staticImports.add(staticImport);
		}
		else
		{
			int index = staticImport.lastIndexOf('.');

			if (index > 0)
			{
				String wildcardImport =
						staticImport.substring(0, index) + ".*";
				
				if (staticImports.contains(wildcardImport))
				{
					// there already is a wildcard import for staticImport
				}
				else
				{
					staticImports.add(staticImport);
				}
			}
		}
	}

	private String getSimpleNameFromQualifiedTypeName(String qualifiedTypeName)
  {
  	return qualifiedTypeName.substring(qualifiedTypeName.lastIndexOf(".") + 1);
  }

  private String getPackageNameFromQualifiedTypeName(String qualifiedTypeName)
  {
  	return qualifiedTypeName.substring(0, qualifiedTypeName.lastIndexOf("."));
  }
}